#include "pow_op.h"
#include "../constant.h"
#include "subtract_op.h"
#include "multiply_op.h"
#include "log_op.h"
#include "../../factory/factory.h"
#include "../../factory/constant_factory.h"
#include "../../factory/subtract_factory.h"
#include "../../factory/pow_factory.h"
#include "../../factory/multiply_factory.h"
#include "../../factory/log_factory.h"

Pow::Pow(Node* lhs, Node* rhs) : OpNode({lhs, rhs}){
}

vector<Node*> Pow::getGradNodes(Node* grad) {
    auto nodes = *getNodeIn();
    Factory* factory;
    Shape* shape = nodes[1] -> getOutShape();
    factory = new ConstantFactory(Matrix(shape -> rows, shape -> cols, 1));
    Node* ones = factory -> createNode();
    delete factory;
    factory = new SubtractFactory(nodes[1], ones);
    Node* subtract = factory -> createNode();
    delete factory;
    factory = new PowFactory(nodes[0], subtract);
    Node* pow = factory -> createNode();
    delete factory;
    factory = new MultiplyFactory(nodes[1], pow);
    Node*  multiply = factory -> createNode();
    delete factory;
    factory = new MultiplyFactory(multiply, grad);
    Node* l_grad = factory -> createNode();
    delete factory;

    factory = new LogFactory(nodes[0]);
    Node* log = factory -> createNode();
    delete factory;
    factory = new MultiplyFactory(this, log);
    Node* multi = factory -> createNode();
    delete factory;
    factory = new MultiplyFactory(multi, grad);
    Node* r_grad = factory -> createNode();
    delete factory;
    return {l_grad, r_grad};
}

void Pow::compute(Context* context){
    auto nodes = *getNodeIn();
    auto l_mat = context -> getFlowedData(nodes[0]);
    auto r_mat = context -> getFlowedData(nodes[1]);
    context -> setFlowedData(this, pow(l_mat, r_mat));
}
